package jdec.jdt.ls.decompiler.common;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IParent;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.compiler.env.IBinaryType;
import org.eclipse.jdt.internal.core.BinaryType;
import org.eclipse.jdt.internal.core.SourceMapper;
import org.eclipse.jdt.ls.core.internal.IDecompiler;

public abstract class DecompilerSourceMapper extends SourceMapper implements IDecompiler {
	protected static Map<IPackageFragmentRoot, SourceMapper> originalSourceMapper = new ConcurrentHashMap<>();

	protected boolean isAttachedSource;

	public DecompilerSourceMapper(IPath sourcePath, String rootPath, Map options) {
		super(sourcePath, rootPath, options);
	}

	public char[] findSource(IType type) throws JavaModelException {
		if (!type.isBinary()) {
			return null;
		}

		if (type instanceof BinaryType declType) {

			while ((declType.getDeclaringType() instanceof BinaryType subType) && subType != null) {
				declType = subType;
			}

			IBinaryType info = declType.getElementInfo();
			if (info == null) {
				return null;
			}

			return findSource(type, info);
		}

		return null;

	}

	/**
	 * @return Does the source returned by {@link #findSource(IType, IBinaryType)}
	 *         originate from a source attachment?
	 */
	public boolean isAttachedSource() {
		return isAttachedSource;
	}

	/**
	 * Finds the deepest <code>IJavaElement</code> in the hierarchy of
	 * <code>elt</elt>'s children (including <code>elt</code> itself) which has a
	 * source range that encloses <code>position</code> according to
	 * <code>mapper</code>.
	 *
	 * Code mostly taken from 'org.eclipse.jdt.internal.core.ClassFile'
	 */
	protected IJavaElement findElement(IJavaElement elt, int position) {
		ISourceRange range = getSourceRange(elt);
		if (range == null || position < range.getOffset() || range.getOffset() + range.getLength() - 1 < position) {
			return null;
		}
		if (elt instanceof IParent) {
			try {
				IJavaElement[] children = ((IParent) elt).getChildren();
				for (int i = 0; i < children.length; i++) {
					IJavaElement match = findElement(children[i], position);
					if (match != null) {
						return match;
					}
				}
			} catch (JavaModelException npe) {
				// ignore
			}
		}
		return elt;
	}

}
